package tools.intersectionTests;
import physics.shapes.Circle;
import physics.shapes.Polygon;
import physics.shapes.Shape;
import tools.Help;
import tools.Point;
/**
 * @author toni Provides methods for point <-> shape intersection tests. e.g. point <-> polygon, point <-> circle. TODO Performance by keeping a point array in store?
 */
public class IntersectionTest {
	/**
	 * Calculates the distance between a point and a polygon.
	 * 
	 * @param p
	 *            The position of the point (global)
	 * @param poly
	 *            : The polygon shape
	 * @return
	 */
	public static float dist(Point p, Polygon poly) {
		return dist(p, poly.points);
	}
	/**
	 * Calculates whether the point and the polygon given intersect.
	 * 
	 * @param p
	 *            The position of the point
	 * @param poly
	 *            The polygon shape
	 * @return
	 */
	public static boolean intersects(Point p, Polygon poly) {
		return dist(p, poly) < 0;
	}
	/**
	 * Calculates whether the point and the polygon given intersect.
	 * 
	 * @param p
	 *            The position of the point
	 * @param lines
	 *            The list of points of the polygon
	 * @return
	 */
	public static boolean intersects(Point p, Point[] lines) {
		return dist(p, lines) < 0;
	}
	/**
	 * Calculates whether the point and the circle given intersect.
	 * 
	 * @param p
	 *            The position of the point
	 * @param pos
	 *            The position of the circle
	 * @param radius
	 *            The radius of the circle
	 * @return
	 */
	public static boolean intersects(Point p, Point pos, float radius) {
		return dist(p, pos, radius) < 0;
	}
	/**
	 * Returns the distance from a Point to a circle
	 * 
	 * @param p
	 *            The point
	 * @param pos
	 *            The center of the circle
	 * @param radius
	 *            The radius of the circle
	 * @return
	 */
	public static float dist(Point p, Point pos, float radius) {
		Point line = pos.sub(p).normalize();
		float pProj = line.mul(p);
		float minProj = line.mul(pos) - radius;
		float maxProj = line.mul(pos) + radius;
		float dist = -Math.min(pProj - minProj, maxProj - pProj);
		return dist;
	}
	/**
	 * Calculates the distance between a point and a polygon.
	 * 
	 * @param p
	 *            The position of the point (global)
	 * @param points
	 *            The set of points of the polygon
	 * @return
	 */
	private static float dist(Point p, Point[] points) {
		Point[] lines = new Point[points.length * 2];
		for (int i = 0, j = points.length - 1; i < points.length; i++, j = i - 1) {
			lines[i] = points[j].sub(points[i]).sNormalize2();
			lines[i + points.length] = points[i].sub(p).sNormalize2();
		}
		float maxDist = Float.NEGATIVE_INFINITY;
		for (int i = 0; i < lines.length; i++) {
			float pProj = lines[i].mul(p);
			float minProj = Float.POSITIVE_INFINITY;
			float maxProj = Float.NEGATIVE_INFINITY;
			for (int j = 0; j < points.length; j++) {
				float d = lines[i].mul(points[j]);
				if (d < minProj)
					minProj = d;
				if (d > maxProj)
					maxProj = d;
			}
			float dist = -Math.min(pProj - minProj, maxProj - pProj);
			if (dist > maxDist)
				maxDist = dist;
		}
		return maxDist;
	}
	/**
	 * Returns the distance from the point to the shape.
	 * 
	 * @param p
	 *            The point
	 * @param shape
	 *            The shape
	 * @return
	 */
	public static float dist(Point p, Shape shape) {
		if (shape instanceof Polygon)
			return dist(p, (Polygon) shape);
		if (shape instanceof Circle) {
			Circle circle = (Circle) shape;
			return dist(p, circle.pos, circle.radius);
		}
		assert (false);
		return 0.0f;
	}
	/**
	 * Returns whether the point intersects with the shape.
	 * 
	 * @param p
	 *            The point
	 * @param shape
	 *            The shape
	 * @return
	 */
	public static boolean intersects(Point p, Shape shape) {
		if (shape instanceof Polygon)
			return intersects(p, (Polygon) shape);
		if (shape instanceof Circle) {
			Circle circle = (Circle) shape;
			return intersects(p, circle.pos, circle.radius);
		}
		assert (false);
		return false;
	}
}
